package com.yourname.projectname.util;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 简易 JWT 工具类（毕业设计适用）
 * 功能：
 * 1. 使用 HMAC-SHA256 签名算法生成和验证 JWT 令牌
 * 2. 支持存储手机号和角色等自定义用户信息
 * 3. 包含令牌有效期验证机制
 * 4. 代码结构简洁且符合安全规范
 * 注意：密钥在生产环境应通过安全方式存储
 */
public class JwtUtil {

    // 密钥（毕业设计可硬编码，但实际应用必须安全存储！）
    @Value("${jwt.secret-key}")
    private static String SECRET_KEY;
    // 令牌有效期（毫秒），例如 24 小时
    private static final long EXPIRATION_TIME = 24 * 60 * 60 * 1000;

    /**
     * 生成 JWT 令牌
     * 
     * @param account 账号 - 用于标识用户身份
     * @param role 角色信息 - 自定义字段用于权限控制
     * @return 生成的 JWT 令牌字符串
     * @throws RuntimeException 当签名计算或编码失败时抛出
     * 
     * 流程说明：
     * 1. 创建 JWT 头部（Header）
     * 2. 构建有效载荷（Payload）包含用户信息和标准字段
     * 3. 对头部和载荷进行 Base64Url 编码
     * 4. 生成签名并组合成完整 JWT
     */
    public static String generateToken(String account, String role) {
        try {
            // 1. 创建 Header（标准 JWT 头部）
            Map<String, Object> header = new HashMap<>();
            header.put("alg", "HS256");
            header.put("typ", "JWT");

            // 2. 创建 Payload（自定义用户信息）
            Map<String, Object> payload = new HashMap<>();
            payload.put("account", account);
            payload.put("role", role); // 自定义字段
            payload.put("iat", System.currentTimeMillis() / 1000); // 标准字段：签发时间
            payload.put("exp", (System.currentTimeMillis() + EXPIRATION_TIME) / 1000); // 标准字段：过期时间

            // 3. Base64Url 编码 Header 和 Payload
            String encodedHeader = base64UrlEncode(header.toString().getBytes(StandardCharsets.UTF_8));
            String encodedPayload = base64UrlEncode(payload.toString().getBytes(StandardCharsets.UTF_8));

            // 4. 创建签名
            String signingInput = encodedHeader + "." + encodedPayload;
            String signature = generateSignature(signingInput);

            // 5. 组合成完整 JWT
            return signingInput + "." + signature;

        } catch (Exception e) {
            throw new RuntimeException("JWT 生成失败", e);
        }
    }

    /**
     * 验证 JWT 令牌的有效性
     * 
     * @param token 要验证的 JWT 令牌字符串
     * @return 包含用户信息的 Map（包含 account 和 role 字段）
     *         验证失败时返回 null
     * @throws RuntimeException 当解析失败或签名算法异常时抛出
     * 
     * 验证流程：
     * 1. 分割 JWT 字符串为三部分
     * 2. 验证签名有效性
     * 3. 检查令牌是否过期
     * 4. 提取并返回用户信息
     */
    public static Map<String, String> validateToken(String token) {
        try {
            // 1. 分割 JWT 的三部分
            String[] parts = token.split("\\.");
            if (parts.length != 3) {
                return null; // 无效的 JWT 格式
            }

            String encodedHeader = parts[0];
            String encodedPayload = parts[1];
            String signature = parts[2];

            // 2. 重新计算签名
            String signingInput = encodedHeader + "." + encodedPayload;
            String expectedSignature = generateSignature(signingInput);

            // 3. 验证签名
            if (!signature.equals(expectedSignature)) {
                return null; // 签名验证失败
            }

            // 4. 解码 Payload 获取用户信息
            String payloadJson = new String(base64UrlDecode(encodedPayload), StandardCharsets.UTF_8);

            // 5. 检查过期时间
            if (payloadJson.contains("\"exp\":")) {
                long exp = extractExpFromPayload(payloadJson);
                if (System.currentTimeMillis() / 1000 > exp) {
                    return null; // 令牌已过期
                }
            }

            // 6. 返回用户信息
            Map<String, String> userInfo = new HashMap<>();
            userInfo.put("account", extractField(payloadJson, "account"));
            userInfo.put("role", extractField(payloadJson, "role"));
            return userInfo;

        } catch (Exception e) {
            return null; // 验证过程中出错
        }
    }

    // ==================== 辅助方法 ====================

    /**
     * Base64Url 编码
     * 
     * @param data 待编码的原始字节数组
     * @return URL 安全的 Base64 编码字符串
     */
    private static String base64UrlEncode(byte[] data) {
        return Base64.encodeBase64URLSafeString(data);
    }

    /**
     * Base64Url 解码
     * 
     * @param encoded 已编码的 Base64Url 字符串
     * @return 解码后的原始字节数组
     */
    private static byte[] base64UrlDecode(String encoded) {
        return Base64.decodeBase64(encoded);
    }

    /**
     * 生成 JWT 签名
     * 
     * @param signingInput 签名输入内容（header.payload）
     * @return 生成的签名字符串
     * @throws Exception 当签名计算失败时抛出
     */
    private static String generateSignature(String signingInput) throws Exception {
        Mac hmac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(
                SECRET_KEY.getBytes(StandardCharsets.UTF_8),
                "HmacSHA256"
        );
        hmac.init(secretKey);
        return base64UrlEncode(hmac.doFinal(signingInput.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 从 Payload 中提取过期时间
     * 
     * @param payloadJson 已解码的 Payload JSON 字符串
     * @return 过期时间戳（秒）
     * 
     * 注意：此方法为简化实现，实际应使用 JSON 解析器
     */
    private static long extractExpFromPayload(String payloadJson) {
        // 简化处理：实际应使用 JSON 解析器
        int start = payloadJson.indexOf("\"exp\":") + 6;
        int end = payloadJson.indexOf(',', start);
        if (end == -1) end = payloadJson.indexOf('}', start);
        return Long.parseLong(payloadJson.substring(start, end));
    }

    /**
     * 从 Payload 中提取指定字段值
     * 
     * @param payloadJson 已解码的 Payload JSON 字符串
     * @param field 要提取的字段名称
     * @return 字段值
     * 
     * 注意：此方法为简化实现，实际应使用 JSON 解析器
     */
    private static String extractField(String payloadJson, String field) {
        int start = payloadJson.indexOf("\"" + field + "\":\"") + field.length() + 4;
        int end = payloadJson.indexOf('"', start);
        return payloadJson.substring(start, end);
    }
}